package me.yricky.cbor

import java.nio.ByteBuffer
import java.nio.ByteOrder

object CBORDecoder {
    
    fun readCBORItem(ba:ByteArray):CBORItem? = readCBORItem(ByteBuffer.wrap(ba))

    fun readCBORItem(bb:ByteBuffer): CBORItem? {

        val v1 = (bb.get().toInt()) and 0xff
        when (v1) {
            BYTE_FALSE -> { return CBORFalse }
            BYTE_TRUE -> { return CBORTrue }
            BYTE_NULL -> { return CBORNull }
        }
        val majorType = (v1 shr 5) and 0b111
        val subType = v1 and LOW5_MASK
        println("type:${majorType},subType:${subType}")
        when(majorType){
            MAJOR_NEG_NUM,
            MAJOR_NUM -> {
                val num = internalReadNum(subType,bb)
                return if(majorType == MAJOR_NUM){
                    CBORNum(num)
                }else{
                    CBORNum(-1 - num)
                }
            }
            MAJOR_BA -> {
                val size = internalReadNum(subType,bb)
                println("size:${size}")
                val ba = ByteArray(size.toInt()).also {
                    bb.get(it)
                }
                return CBORByteArray(ba)
            }
            MAJOR_STR -> {
                val size = internalReadNum(subType,bb)
                println("str size:${size}")
                val ba = ByteArray(size.toInt()).also {
                    bb.get(it)
                }
                return CBORString(String(ba))
            }
            MAJOR_ARR -> {
                val size = internalReadNum(subType,bb)
                println("arr size:${size}")
                return CBORArray(
                    (0 until size)
                        .mapNotNull{ readCBORItem(bb) }
                        .also { assert(it.size == size.toInt()) }
                )
            }
            MAJOR_KV -> {

                val size = internalReadNum(subType,bb).toInt()
                println("kv size:${size}")
                return CBORKv(
                    (0 until size)
                        .mapNotNull{ Pair(readCBORItem(bb),readCBORItem(bb)).takeIf { it.first != null && it.second != null }?.let { Pair(it.first!!,it.second!!) } }
                        .also { println("${it.size} == $size,${it.size == size}") }
                )
            }
            else -> return null
        }
    }

    private fun internalReadNum(subType:Int,bb: ByteBuffer):Long{
        println("internal_num_sub:${subType}")
        return when (subType) {
            in (0 .. 23) -> {
                subType.toLong()
            }
            24 -> {
                bb.get().toUByte().toLong()
            }
            25 -> {
                bb.short.toUShort().toLong()
            }
            26 -> {
                bb.int.toUInt().toLong()
            }
            27 -> {
                bb.long
            }
            else -> {
                println("wtf!")
                0L
            }
        }
    }

}
